package org.zoomdev.zoom.dao;

import org.zoomdev.zoom.common.filter.Filter;
import org.zoomdev.zoom.dao.configs.*;
import org.zoomdev.zoom.dao.driver.SqlDriver;
import org.zoomdev.zoom.dao.entity.Entity;
import org.zoomdev.zoom.dao.entity.Field2ColumnMappingConfig;
import org.zoomdev.zoom.dao.executor.BatchResult;
import org.zoomdev.zoom.dao.transaction.TransBase;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class SqlConfig extends TransBase {
    private Where where;
    private Query query;
    private LinkedHashMap<String, BatchConfig> batchCache;
    private Update update;
    private Insert insert;
    private Delete delete;
    private String table;
    private SqlDriver driver;
    private Entity entity;
    private boolean ignoreNull = true;
    private Object paramObj;
    private Filter<Field2ColumnMappingConfig> filter;
    private boolean batch;
    private Object tmpData;
    private StringBuilder sqlBuilder;

    public SqlConfig() {

    }

    public void clear() {
        releaseConnection();
        if (update != null) {
            update.clear();
        }

        if (query != null) {
            query.clear();
        }

        if (where != null) {
            where.clear();
        }

        if (delete != null) {
            delete.clear();
        }

        if (sqlBuilder != null) {
            sqlBuilder.setLength(0);
        }

        filter = null;
        batch = false;
        tmpData = null;
    }

    public Entity getEntity() {
        return entity;
    }

    public void setEntity(Entity entity) {
        this.entity = entity;
        Where where = this.where;
        if (where != null) {
            where.setEntity(entity);
        }

    }

    public Query getQuery() {
        Query query = this.query;
        if (query == null) {
            return this.query = new Query(driver);
        }
        return query;
    }

    public Update getUpdate() {
        Update update = this.update;
        if (update == null) {
            return this.update = new Update(driver);
        }
        return update;
    }

    public Where getWhere() {
        Where where = this.where;
        if (where == null) {
            return this.where = new Where(driver, this, entity);
        }
        return where;
    }

    public Insert getInsert() {
        Insert insert = this.insert;
        if (insert == null) {
            return this.insert = new Insert(driver);
        }
        return insert;
    }

    public Delete getDelete() {
        Delete delete = this.delete;
        if (delete == null) {
            return this.delete = new Delete(driver);
        }
        return delete;
    }

    public Object getParamObj() {
        return paramObj;
    }

    public void setParamObj(Object paramObj) {
        this.paramObj = paramObj;
    }

    public boolean isIgnoreNull() {
        return ignoreNull;
    }

    public void setIgnoreNull(boolean ignoreNull) {
        this.ignoreNull = ignoreNull;
    }

    public void setDao(Dao dao) {
        super.setDao(dao);
        this.driver = dao.getDriver();
    }

    public String getTable() {
        return table;
    }

    public void setTable(String table) {
        this.table = table;
    }

    public Filter<Field2ColumnMappingConfig> getFilter() {
        return filter;
    }

    public void setFilter(Filter<Field2ColumnMappingConfig> filter) {
        this.filter = filter;
    }

    public BatchConfig prepareStatement(Connection conn, String sql) throws SQLException {
        LinkedHashMap<String, BatchConfig> cache = this.batchCache;
        if (cache == null) {
            cache = new LinkedHashMap<>();
            this.batchCache = cache;
        }
        BatchConfig cfg = cache.get(sql);
        if (cfg == null) {
            cfg = new BatchConfig();
            cfg.batchResult = new BatchResult();
            cfg.stmt = entity.prepareInsert(conn, sql);
            cfg.sql = sql;
            cache.put(sql, cfg);
        }
        return cfg;
    }

    public List<BatchResult> getBatchResults() {
        LinkedHashMap<String, BatchConfig> batchCache = this.batchCache;
        if (batchCache == null) {
            throw new DaoException("批操作未开启");
        }
        try {
            List<BatchResult> list = new ArrayList<>(batchCache.size());
            for (Map.Entry<String, BatchConfig> entry : batchCache.entrySet()) {
                BatchConfig cfg = entry.getValue();
                int[] counts = cfg.stmt.executeBatch();
                cfg.batchResult.setCounts(counts);
                close(cfg.stmt);
                list.add(cfg.batchResult);
            }
            return list;
        } catch (Throwable e) {
            for (Map.Entry<String, BatchConfig> entry : batchCache.entrySet()) {
                BatchConfig cfg = entry.getValue();
                close(cfg.stmt);
            }
            throw new DaoException("获取结果异常", e);
        } finally {
            batchCache.clear();
            this.batchCache = null;
        }
    }

    public boolean isBatch() {
        return batch;
    }

    public void setBatch(boolean batch) {
        this.batch = batch;
    }

    public Object getTmpData() {
        return tmpData;
    }

    public void setTmpData(Object tmpData) {
        this.tmpData = tmpData;
    }

    public StringBuilder getSqlBuilder() {
        StringBuilder sqlBuilder = this.sqlBuilder;
        if (sqlBuilder == null) {
            return this.sqlBuilder = new StringBuilder(512);
        }
        return sqlBuilder;
    }

    private static void close(AutoCloseable close) {
        try {
            if (close != null) {
                close.close();
            }
        } catch (Throwable ignored) {

        }
    }

    public static class BatchConfig {
        public String sql;
        public PreparedStatement stmt;
        public BatchResult batchResult;

        public void addBatch(Object data) throws SQLException {
            batchResult.add(data);
            stmt.addBatch();
        }

    }
}
